/*********************************************************************
*
* Software License Agreement (BSD License)
*
*  Copyright (c) 2010, Heriot-Watt University, UK.
*  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the Heriot-Watt University nor the names of
*     its contributors may be used to endorse or promote products
*     derived from this software without specific prior written
*     permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
*  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
*  POSSIBILITY OF SUCH DAMAGE.
*
*  Author: Joel Cartwright
*
*********************************************************************/

#ifndef MATCHMAKER_SERVICE_MONITOR_H_
#define MATCHMAKER_SERVICE_MONITOR_H_

#include <list>

#include <boost/shared_ptr.hpp>
#include <boost/lambda/lambda.hpp>

#include <ros/ros.h>

#include <matchmaker/service_details.h>

#include <matchmaker_msgs/Advert.h>

namespace matchmaker
{

class ServiceMonitor;

/**
 * @brief Class representing the ongoing monitoring of a service using the Client
 *
 * Provides a method to manually retrieve the service status, as well as a mechanism
 * to register a function to be called on change of the service status.
 *
 * Non-empty instances of this class can only be created by the Client.
 */
class ServiceMonitor
{
public:
  /**
   * @brief Constructor for empty ServiceMonitor
   */
  ServiceMonitor();

  /**
   * @brief Copy constructor
   * @param rhs instance to copy
   */
  ServiceMonitor(const ServiceMonitor& rhs);

  /**
   * @brief Destructor
   */
  ~ServiceMonitor();

  /**
   * @brief Stop monitoring the associated service
   *
   * This method is automatically called by the destructor. Note that if
   * there are multiple (user) copies of a particular ServiceMonitor,
   * only when they all go out of scope or have shutdown called will the
   * matchmaker Client library stop monitoring the service.
   */
  void shutdown();

  /**
   * @brief Get the ID of the monitored service
   * @return service ID, or empty string if monitor not valid
   */
  std::string getID() const;

  /**
   * @brief Get the name of the monitored service
   * @return service name, or empty string if monitor not valid
   */
  std::string getName() const;

  /**
   * @brief Get the topic of the monitored service
   * @return service topic, or empty string if monitor not valid
   */
  std::string getTopic() const;

  /**
   * @brief Get the status of the monitored service
   * @return service status, or ServiceStatus::REMOVED if monitor not valid
   */
  ServiceStatus getStatus() const;

  /**
   * @brief Get the provider of the monitored service
   * @return service provider, or ServiceStatus::REMOVED if monitor not valid
   */
  std::string getProvider() const;

  /**
   * @brief Get the ServiceDetails object for the monitored service
   * @return ServiceDetails pointer, or NULL if monitor not valid
   */
  const ServiceDetails* getServiceDetails() const;

  /**
   * @brief Get the time the status was last updated
   * @return last update time, or zero time if monitor not valid
   */
  ros::Time getStatusChangeTime() const;

  /**
   * @brief Test if the instance is valid
   *
   * This overloaded operator allows instances of the monitor to be
   * tested for validity like so:
\verbatim
if (monitor) { ... }
\endverbatim
   * @return (void*)1 if valid, (void*)0 if not
   */
  operator void*() const
  {
    return (impl_ && !getStatusChangeTime().isZero()) ? (void*)1 : (void*)0;
  }

  // -----------------------------------------
  // Users should ignore code below this line.
  // -----------------------------------------

  bool operator<(const ServiceMonitor& rhs) const
  {
    return impl_ < rhs.impl_;
  }

  bool operator==(const ServiceMonitor& rhs) const
  {
    return impl_ == rhs.impl_;
  }

  bool operator!=(const ServiceMonitor& rhs) const
  {
    return impl_ != rhs.impl_;
  }

private:
  ServiceMonitor(const ServiceDetailsPtr& service,
      boost::function<void (const ServiceDetails& service)> callback = boost::lambda::constant(true));

  ServiceMonitor(const ServiceDetails* service,
      boost::function<void (const ServiceDetails& service)> callback = boost::lambda::constant(true));

  void storeTimer(ros::Timer timer);

  void setStatus(const ServiceStatus& status);

  void performCallback();

  ros::Time getCreationTime() const;

  long int getNumCopies();

  /**
   * @brief Internal details of the ServiceMonitor, not for external use
   */
  class Impl
  {
  public:
    Impl();
    ~Impl();

    ros::Time creationTime_;
    ServiceDetails service_;
    ros::Time statusChangeTime_;
    boost::function<void (const ServiceDetails& service)> callback_;
    ros::Timer callbackTimer_;
  };

  typedef boost::shared_ptr<Impl> ImplPtr;
  typedef boost::weak_ptr<Impl> ImplWPtr;

  ImplPtr impl_;

  friend class Client;
};

typedef std::list<ServiceMonitor> L_ServiceMonitor;

}

#endif /* MATCHMAKER_SERVICE_MONITOR_H_ */
